package game.engine;

import game.evaluator.IMorrisGameEvaluator;
import game.rule.MorrisGameRule;

import java.util.Vector;

public class GameEngineImpl implements IGameEngine {

	protected int leafCnt;
	protected int estimateScore;
	protected String bestBoard;
	protected IMorrisGameEvaluator evaluator;

	public GameEngineImpl(IMorrisGameEvaluator evaluator) {
		this.leafCnt = 0;
		this.estimateScore = 0;
		this.bestBoard = null;
		this.evaluator = evaluator;
	}

	@Override
	public void runOpening(String board, int dep) {
		this.leafCnt = 0;
		this.bestBoard = null;
		this.estimateScore = maxMinOpening(board, 0, dep);
	}

	@Override
	public void runGame(String board, int dep) {
		this.leafCnt = 0;
		this.bestBoard = null;
		this.estimateScore = maxMinGame(board, 0, dep);
	}

	protected int maxMinOpening(String board, int curDep, int dep) {
		if (curDep == dep) {
			leafCnt++;
			return evaluator.estimateOpening(board);
		}
		Vector<String> moveList = MorrisGameRule.generateMovesOpening(board);
		if (moveList.size() == 0) {
			return evaluator.estimateOpening(board);
		}
		int max = Integer.MIN_VALUE;
		for (String move : moveList) {
			int val = minMaxOpening(move, curDep + 1, dep);
			if (val > max) {
				max = val;
				if (curDep == 0) {
					bestBoard = move;
				}
			}
		}
		return max;
	}

	protected int minMaxOpening(String board, int curDep, int dep) {
		if (curDep == dep) {
			leafCnt++;
			return evaluator.estimateOpening(board);
		}
		Vector<String> moveList = MorrisGameRule.generateBlackMovesOpening(board);
		if (moveList.size() == 0) {
			return evaluator.estimateOpening(board);
		}
		int min = Integer.MAX_VALUE;
		for (String move : moveList) {
			min = Math.min(min, maxMinOpening(move, curDep + 1, dep));
		}
		return min;
	}

	protected int maxMinGame(String board, int curDep, int dep) {
		if (curDep == dep) {
			leafCnt++;
			return evaluator.estimateMidgameEndgame(board);
		}
		Vector<String> moveList = MorrisGameRule.generateMovesMidgameEndgame(board);
		if (moveList.size() == 0) {
			return evaluator.estimateMidgameEndgame(board);
		}
		int max = Integer.MIN_VALUE;
		for (String move : moveList) {
			int val = minMaxGame(move, curDep + 1, dep);
			if (val > max) {
				max = val;
				if (curDep == 0) {
					bestBoard = move;
				}
			}
		}
		return max;
	}

	protected int minMaxGame(String board, int curDep, int dep) {
		if (curDep == dep) {
			leafCnt++;
			return evaluator.estimateMidgameEndgame(board);
		}
		Vector<String> moveList = MorrisGameRule
				.generateBlackMovesMidgameEndgame(board);
		if (moveList.size() == 0) {
			return evaluator.estimateMidgameEndgame(board);
		}
		int min = Integer.MAX_VALUE;
		for (String move : moveList) {
			min = Math.min(min, maxMinGame(move, curDep + 1, dep));
		}
		return min;
	}

	@Override
	public int getLeafCnt() {
		return this.leafCnt;
	}

	@Override
	public int getEstimatScore() {
		return this.estimateScore;
	}

	@Override
	public String getBestBoard() {
		return this.bestBoard;
	}

}
